package cz.possoft.client.clovece.gui.panel;

import java.util.Collection;
import java.util.concurrent.CountDownLatch;

import javax.swing.JButton;

import cz.possoft.client.clovece.gui.components.CloveceFigurkaOnPane;
import cz.possoft.client.clovece.gui.components.ClovecePane;
import cz.possoft.client.clovece.gui.components.CubePane;
import cz.possoft.client.gui.dialog.SimpleDialog;
import cz.possoft.client.gui.panel.GamePanel;
import cz.possoft.client.gui.panel.nastavenispodnipanel.panel.NastaveniShowPanel;
import cz.possoft.client.utils.PanelHolderUtils;
import cz.possoft.generic.utils.GenericUtils;
import cz.possoft.messages.PropertiesManager;
import cz.possoft.messages.Text;
import cz.possoft.serializable.clovece.cube.CubeGenerator;
import cz.possoft.serializable.clovece.cube.StdinCubeGenerator;
import cz.possoft.serializable.clovece.game.CloveceField;
import cz.possoft.serializable.clovece.game.CloveceGame;
import cz.possoft.serializable.clovece.game.ClovecePole;

/**
 * - Na zacatku se hraje 3 krat dokud se nehodi 6ka
 * - Po kazdym tahu si muzu vybrat s cim zrovna pojedu. Figurku soupere muzu vyhodit aji kdyz mne padne 6ka. Po padnti 6ky jedu jeste jednou a muzu klidne vyhodit dalsi figurku.
 * 
 * @author <a href="mailto:mposolda@gmail.com">Marek Posolda</a>
 */
public class ClovecePanel extends GamePanel<ClovecePane, CloveceGame, ClovecePole>
{
   private static final long serialVersionUID = 8923894234l;
   
   private CubePanel cubePanel;
   private CubePane cubePane;
   private JButton pressCubeButton;
   
   // if true, then we should play with cube. If false, then we should play with figurka.
   private boolean statePlayWithCubeFlag;
   
   // Generator for cube result
//   CubeGenerator cubeGenerator = new RandomCubeGenerator();
   CubeGenerator cubeGenerator = new StdinCubeGenerator();
   
   private static final String HODIT_KOSTKOU = "TD - Hodit kostkou";
   
   // Tato metoda zavola setGame a pote provede nezbytne inicialni
   // kroky pri nasetovani teto hry (prekresleni panelu atd.)
   @Override
   public void setAndInitGame(CloveceGame gam) 
   {    
       super.setAndInitGame(gam);        
       
       if (!gam.isMeWhite()) 
       {                 
          getClientNameLabel().setText(addHtmlTagsToLabels(gam.getBlackPlayerName(), "RED", '5'));        
       }
       else
       {            
          getOpponentNameLabel().setText(addHtmlTagsToLabels(gam.getBlackPlayerName(), "RED", '5'));        
       }       
       
       getGamePane().setHraciPole(gam.getHraciPole());   
       
       // Game (and each 'tah') always starts by playing with cibe and not playing with figurka
       statePlayWithCubeFlag = true;     
       getGamePane().setSelectedFigurkaForPlay(null);              
   }
   
   @Override
   protected void initComponents() 
   {
      super.initComponents();
      
      // Remove opponentLabel from this panel and add it to subpanel cubePanel
      remove(getOpponentNameLabel());      
      add(getCubePanel(), "6,2");
   }
   
   /**
    * 0 - Game is not active
    * 1 - I am on play and I will play with the cube
    * 2 - I am on play and I will play with some figurka
    * 3 - I am on play and I will play with some figurka. Figurka is already selected for play.
    * 4 - Opponent is on play
    * 
    * @return the state as described above
    */
   public int getStateOfGame()
   {
      if (!(getGame().isActive()))
      {
         return 0;
      }
      else if (!(getGame().jsemNaTahu()))
      {
         return 4;
      }
      else if ((getGame().jsemNaTahu()) &&  (statePlayWithCubeFlag))
      {
         return 1;
      }
      else if (getGamePane().getSelectedFigurkaForPlay() == null)
      {
         return 2;
      }
      else
      {
         return 3;
      }
   }
   
   /**
    * This is called when playing with cube of current player is triggered by UI event.
    * - We need to generate random result (1-6)
    * - Send event with result to opponent
    * - Simulate playing with our own cube in our UI by calling of 'doPlay' with needed parameters (Same method 'doPlay' is also triggered for opponent in his environment)    
    */
   public void playWithCube()
   {
      // generate random number
      int number = cubeGenerator.nextValue();
      
      NastaveniShowPanel.getConnectThread().sendToServer("PLAY: _1_" + number + "_0_0");
      doPlay(1, number, 0, 0);                 
   }
   
   /**
    * This is called from listener when player want to play to some place on the board.
    * We need to perform validation that move is really valid.
    * 
    * Note: Assumption is that "figurkaFrom" is set in variable "selectedFigurkaForPlay" on ClovecePane. So it doesn't need to be send as parameter here. 
    * 
    * @param figurkaTo - Place where we need to play. 
    */
   public void doValidationAndPlayWithFigurka(CloveceFigurkaOnPane figurkaTo)
   { 
      // validation. We can do something only if we can play to field 'figurkaTo' or we click to the same field (This means unselection of previous figurka)
      if ((figurkaTo != null) && 
            ((figurkaTo.getRingForPlaceWhereWeCanPlay() != GenericUtils.COLOR_NEUTRAL) || (figurkaTo.getRingSelectedForPlay() != GenericUtils.COLOR_NEUTRAL)))
      {                  
         CloveceFigurkaOnPane figurkaFrom = getGamePane().getSelectedFigurkaForPlay();
         int pocetTahu = getCubePane().getStateOnCube();
         // now we need to really play with our figurka. We need: 
         // 1) Clear all rings, 
         // 2) Check that figurkaFrom and figurkaTo are not the same. If yes, then it means that previous selection of figurka 
         //    has been cancelled by player and so he will need to select another figurka.
         // 2) Send to server that playing is in progress
         // 3) Call doPlay
         
         figurkaFrom.setRingSelectedForPlay(GenericUtils.COLOR_NEUTRAL);
                           
         // Now clear rings on the fields where we can play.
         Collection<CloveceField> fields = getGame().getHraciPole().getFieldsWhereFigurkaCanPlay(figurkaFrom.getField(), pocetTahu, figurkaFrom.getField().getColor());
         for (CloveceField field : fields)
         {
            CloveceFigurkaOnPane clFigurka = getGamePane().getFigurkaOnPaneById(field.getId());
            clFigurka.setRingForPlaceWhereWeCanPlay(GenericUtils.COLOR_NEUTRAL);
         }
         
         // set null to figurka selected for play.
         getGamePane().setSelectedFigurkaForPlay(null);   
         
         // this means that selected figurka has been unselected and so player need to select another figurka
         if (figurkaFrom.equals(figurkaTo))
         {
            makeRingsForFigurkasWhichCanPlay(figurkaFrom.getField().getColor(), pocetTahu);
            return;            
         }
         
         // sending our playing to server
         int from = figurkaFrom.getField().getId(); 
         int to = figurkaTo.getField().getId();
         NastaveniShowPanel.getConnectThread().sendToServer("PLAY: _2_" + from + "_" + to + "_" + pocetTahu);         
         
         doPlay(2, from, to, pocetTahu);         
      }      
   }
   
   /**
    * Fields have different semantics for game "Clovece"
    * oldX - if 1, then it means that active player is playing with cube. If 2, then it means that active player is playing with figurka.
    * oldY - if oldX is 1, then it's number on the cube. We need to simulate "playing with cube" event in UI. if oldX is 2, then it's location (id) of figurka, which will play.
    * newX - Used only if oldX is 2. It's id of new location (field) where figurka will play.
    * newY - Used only if oldX is 2. It contains 'pocetTahu'. 
    */
   public void doPlay(int oldX, int oldY, int newX, int newY) 
   {
      if (oldX == 1)
      {
         CountDownLatch latch = new CountDownLatch(1);
         getCubePane().playCube(oldY, latch);
         
         // Wait until playing with cube is finished in UI
         try
         {
            latch.await();
         }
         catch (InterruptedException ie)
         {
            ie.printStackTrace();
         }
         
         
         // Compute figurkas which can play
         int colorNaTahu = getGame().isWhiteNaTahu() ? GenericUtils.COLOR_WHITE : GenericUtils.COLOR_BLACK;         
         
         // Decide if player can play. If not, then opponent is put directly into the state where he can play.
         if (!(getGame().isPlayerStillOnGameAfterPlayingWithCube(colorNaTahu, oldY)))
         {
            super.doPlay(oldX, oldY, newX, newY);
         }
         else
         {
            makeRingsForFigurkasWhichCanPlay(colorNaTahu, oldY);
         }                                   
                  
      }
      
      // And this is moving with figurka. There is no additional check that move is valid etc. because this has been already done.
      else if (oldX == 2)
      {         
         CloveceFigurkaOnPane figurkaFrom = getGamePane().getFigurkaOnPaneById(oldY);
         CloveceFigurkaOnPane figurkaTo = getGamePane().getFigurkaOnPaneById(newX);
         
         // Some logging
         System.out.println("Playing from field " + figurkaFrom.getField() + " to " + figurkaTo.getField() +". Pocet tahu is " + newY);
         
         // We will play with cube or opponent will play
         statePlayWithCubeFlag = true;         
         int gameState = getGame().handlePlayingWithFigurkas(newY, figurkaFrom.getField(), figurkaTo.getField());

         // TODO: Do some nice UI effect for playing with figurka         
         getGamePane().repaint();
         
         if (gameState == 2)
         {
            handleGameFinishedInUI();
         }         
                  
         // We need to call super to notify opponent, that he can play right now.
         if (gameState == 0)
         {
            super.doPlay(oldX, oldY, newX, newY);
         }
      }
   }
   
   /**
    * We need to create rings for figurkas, which can play. This is called directly after playing with cube. 
    * 
    * @param colorNaTahu
    * @param pocetTahu
    */
   private void makeRingsForFigurkasWhichCanPlay(int colorNaTahu, int pocetTahu)
   {
      if (getGame().jsemNaTahu())
      {
         Collection<CloveceField> figurkasWhichCanPlay = getGame().getAllFigurkasWhichCanPlay(colorNaTahu, pocetTahu);                    
         // This can be 0 during startup of the game when we play with cube more times if all figurkas are at home and we don't have 6
         if (figurkasWhichCanPlay.size() > 0)
         {
            statePlayWithCubeFlag = false;
         
            for (CloveceField figurka : figurkasWhichCanPlay)
            {
               CloveceFigurkaOnPane clf = getGamePane().getFigurkaOnPaneById(figurka.getId());
               clf.setRingColor(figurka.getColor());
            }
         
            getGamePane().repaint();               
            System.out.println("Now some figurka needs to be selected. Figurkas which can play: " + figurkasWhichCanPlay);
         }
      }      
   }
   
   /**
    * do steps for finish this game by win of current player
    *
    */
   private void handleGameFinishedInUI()
   {
      PanelHolderUtils.getCurrentGamePanel().setActiveToGame(false);
      PanelHolderUtils.getCurrentGamePanel().setEndStateToGame(true);
      if (((getGame().isMeWhite()) && (getGame().isWhiteNaTahu())) || ((!getGame().isMeWhite()) && (!getGame().isWhiteNaTahu()))) 
      {
          SimpleDialog.getInstance(PropertiesManager.getInstance().getMessageForKey(Text.VYHRA), PropertiesManager.getInstance().getMessageForKey("VYHRA_TEXT", getGame().getGameKey()));            
      }
      else 
      {
          SimpleDialog.getInstance(PropertiesManager.getInstance().getMessageForKey(Text.PROHRA), PropertiesManager.getInstance().getMessageForKey("PROHRA_TEXT", getGame().getGameKey()));  
      }      
   }
   
   private CubePanel getCubePanel() 
   {
      if (cubePanel == null) 
      {
         cubePanel = new CubePanel();
         cubePanel.init(getOpponentNameLabel(), getCubePane(), getPressCubeButton());
      }
      return cubePanel;
   } 
   
   public CubePane getCubePane()
   {
      if (cubePane == null) 
      {
         cubePane = new CubePane();   
         cubePane.initialize();         
      }
      return cubePane;      
   }
   
   private JButton getPressCubeButton()
   {
      if (pressCubeButton == null) 
      {
         pressCubeButton = new JButton(HODIT_KOSTKOU);

// TODO:
//         pressCubeButton.addActionListener(...);
      }
      return pressCubeButton;      
   }   

}

